package storage

import (
    "context"
    "fmt"
    log "github.com/sirupsen/logrus"

    "cing.io/cing-api/core"
    "cing.io/cing-api/planner"

    clientv3 "go.etcd.io/etcd/client/v3"
    "google.golang.org/protobuf/proto"
)

var PlanNotFoundError = fmt.Errorf("plan not found")
var PlanDeleteError = fmt.Errorf("plan delete error")

func PutPlan(plan *planner.SingingPlan) error {
    time := plan.StartTime.AsTime().UnixMilli()

    key := getPlanKey(time, plan.TaskFullName)

    bytes, err := proto.Marshal(plan)
    if err != nil {
        return err
    }

    _, err = Etcd().Put(context.TODO(), key, string(bytes))
    if err != nil {
        return err
    }

    // create plan log
    err = PutPlanLog(plan.TaskFullName, core.PlanLog_CREATE, time, "")
    if err != nil {
        return err
    }
    return nil
}

func UpdatePlanStatus(taskFullName string, timestamp int64, status planner.SingingPlan_Status) error {
    plan := GetPlan(taskFullName, timestamp)
    if plan == nil {
        return PlanNotFoundError
    }
    plan.Status = status

    err := PutPlan(plan)
    if err != nil {
        return err
    }

    return nil
}

func GetPlan(taskFullName string, timestamp int64) *planner.SingingPlan {
    key := getPlanKey(timestamp, taskFullName)

    resp, err := Etcd().Get(context.TODO(), key)
    if err != nil {
        return nil
    }

    if resp.Count == 0 {
        return nil
    }

    plan := &planner.SingingPlan{}
    err = proto.Unmarshal(resp.Kvs[0].Value, plan)
    if err != nil {
        return nil
    }
    return plan
}

func ListPlanWithTimeRange(startTime, endTime int64) []*planner.SingingPlan {
    start := getPlanKey(startTime, "")
    end := getPlanKey(endTime, "")

    resp, err := Etcd().Get(context.TODO(), start, clientv3.WithRange(end))
    if err != nil {
        return nil
    }

    plans := make([]*planner.SingingPlan, resp.Count)
    for i := range plans {
        plan := &planner.SingingPlan{}
        err = proto.Unmarshal(resp.Kvs[i].Value, plan)
        if err != nil {
            log.Error("plan unmarshal error", err)
        }
        plans[i] = plan
    }

    return plans
}

func DeletePlan(taskFullName string, timestamp int64) error {
    key := getPlanKey(timestamp, taskFullName)
    resp, err := Etcd().Delete(context.TODO(), key)
    if err != nil {
        return err
    }
    if resp.Deleted == 1 {
        return nil
    }
    return PlanDeleteError
}

func DeleteLastPlan(taskFullName string) error {
    time, err := GetPlanLastCreateTime(taskFullName)
    if err != nil {
        return err
    }

    err = DeletePlan(taskFullName, time)
    if err != nil {
        return err
    }

    return nil
}

func getPlanKey(timestamp int64, taskFullName string) string {
    return fmt.Sprintf("%s/%d/%s", planPrefix, timestamp, taskFullName)
}
